﻿using Microsoft.Extensions.DependencyInjection;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;

namespace System
{
    public static class DependencyInjectionExtension
    {
        /// <summary>
        /// 注册程序集里的接口和实现
        /// </summary>
        /// <param name="services"></param>
        /// <param name="assemblies"></param>
        /// <param name="lifetime"></param>
        /// <returns></returns>
        public static IServiceCollection RegisterAssenblyTypes(this IServiceCollection services, Assembly[] assemblies, ServiceLifetime lifetime = ServiceLifetime.Transient)
        {
            // 接口+抽象类
            List<Type> baseTypes = new List<Type>();
            List<Type> implTypes = new List<Type>();

            foreach (Assembly assembly in assemblies)
            {
                var types = assembly.GetExportedTypes();

                //var interfaceTypes = types.Where(s => s.IsInterface).ToArray();
                //if (interfaceTypes.Length > 0)
                //    baseTypes.AddRange(interfaceTypes);

                var abstractTypes = types.Where(s => s.IsAbstract).ToArray();
                if (abstractTypes.Length > 0)
                    baseTypes.AddRange(abstractTypes);

                var subTypes = types.Where(s => !s.IsInterface && !s.IsAbstract && !s.IsPointer && s.GetConstructors().Any(c => c.IsPublic)).ToArray();
                if (subTypes.Length > 0)
                    implTypes.AddRange(subTypes);
            }

            return services.RegisterTypes(baseTypes, implTypes, lifetime);
        }

        /// <summary>
        /// 注册程序集里指定后缀类名的接口和实现
        /// </summary>
        /// <param name="services"></param>
        /// <param name="assemblies"></param>
        /// <param name="suffixName"></param>
        /// <param name="lifetime"></param>
        /// <returns></returns>
        public static IServiceCollection RegisterAssenblyTypesBySuffixName(this IServiceCollection services, Assembly[] assemblies, string suffixName, ServiceLifetime lifetime = ServiceLifetime.Transient)
        {
            // 接口+抽象类
            List<Type> baseTypes = new List<Type>();
            List<Type> implTypes = new List<Type>();

            foreach (Assembly assembly in assemblies)
            {
                var types = assembly.GetExportedTypes().Where(s => s.Name.EndsWith(suffixName)).ToArray();

                var interfaceTypes = types.Where(s => s.IsInterface).ToArray();
                if (interfaceTypes.Length > 0)
                    baseTypes.AddRange(interfaceTypes);

                var abstractTypes = types.Where(s => s.IsAbstract).ToArray();
                if (abstractTypes.Length > 0)
                    baseTypes.AddRange(abstractTypes);

                var subTypes = types.Where(s => !s.IsInterface && !s.IsAbstract && !s.IsPointer
                        && s.GetConstructors().Any(c => c.IsPublic))
                    .ToArray();
                if (subTypes.Length > 0)
                    implTypes.AddRange(subTypes);
            }

            return services.RegisterTypes(baseTypes, implTypes, lifetime);
        }

        /// <summary>
        /// 注册指定接口和实现类
        /// </summary>
        /// <param name="services"></param>
        /// <param name="baseTypes"></param>
        /// <param name="implTypes"></param>
        /// <param name="lifetime"></param>
        /// <returns></returns>
        public static IServiceCollection RegisterTypes(this IServiceCollection services, List<Type> baseTypes, List<Type> implTypes, ServiceLifetime lifetime)
        {
            // 注册
            foreach (var baseType in baseTypes)
            {
                if (baseType.IsInterface)
                {
                    var implType = implTypes.Where(s => s.GetInterfaces().Contains(baseType)).FirstOrDefault();
                    if (implType != null)
                        services.RegisterType(baseType, implType, lifetime);
                }
                else if (baseType.IsAbstract)
                {
                    var implType = implTypes.Where(s => s.BaseType?.GUID == baseType.GUID).FirstOrDefault();
                    if (implType != null)
                        services.RegisterType(baseType, implType, lifetime);
                }
            }

            return services;
        }

        /// <summary>
        /// 注册指定类型
        /// </summary>
        /// <typeparam name="TService"></typeparam>
        /// <typeparam name="TImplementation"></typeparam>
        /// <param name="services"></param>
        /// <param name="lifetime"></param>
        /// <returns></returns>
        public static IServiceCollection RegisterType<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(this IServiceCollection services, ServiceLifetime lifetime)
        {
            return services.RegisterType(typeof(TService), typeof(TImplementation), lifetime);
        }

        /// <summary>
        /// 注册指定类型
        /// </summary>
        /// <param name="services"></param>
        /// <param name="baseType"></param>
        /// <param name="implType"></param>
        /// <param name="lifetime"></param>
        /// <returns></returns>
        public static IServiceCollection RegisterType(this IServiceCollection services, Type baseType, Type implType, ServiceLifetime lifetime = ServiceLifetime.Transient)
        {
            switch (lifetime)
            {
                case ServiceLifetime.Transient:
                    services.AddTransient(baseType, implType);
                    break;
                case ServiceLifetime.Scoped:
                    services.AddScoped(baseType, implType);
                    break;
                case ServiceLifetime.Singleton:
                    services.AddSingleton(baseType, implType);
                    break;
            }
            return services;
        }

        public static List<Type> GetImplementationTypes(Type baseType, params Assembly[] assemblies)
        {
            List<Type> types = new List<Type>(100);

            foreach (Assembly assembly in assemblies)
            {
                var publicTypes = assembly.GetExportedTypes();
                var classTypes = publicTypes.Where(s => s.IsAbstract == false);
                if (baseType.IsInterface)
                    types.AddRange(classTypes.Where(s => s.GetInterfaces().Any(x => x.GUID == baseType.GUID)));
                else
                    types.AddRange(classTypes.Where(s => s.BaseType?.GUID == baseType.GUID));
            }
            return types;
        }

    }
}